package managedbeans;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import nfa.NondeterministicFiniteAutomata;
import utilities.ProcessExecutor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import utilities.InfixToPostfix;

/**
 * Controlador entre los algoritmos y objetos de transformación de expresiones Regulares
 * y ADFs con las vistas que presentan las gráficas y tablas ADFs y las expresiones
 * regulares resultantes.
 * 
 * @author Ricardo Sansores
 */
public class ConverterMB {

    private static final String CONJUNTO_VACIO = "Conjunto Vacío";
    private String inputRegularExpression;
    private String outputRegularExpression;
    private String adf;
    private String adfImagePath = "empty.png";
    private String lastConversion;
    private String adfTable;

    /** Creates a new instance of ConverterMB */
    public ConverterMB() {
        lastConversion = "expression";
        outputRegularExpression = CONJUNTO_VACIO;
    }

    public void regularExpressionToAdf() {
        setLastConversion("expression");
        try {
            //FIXME extraer strings a constantes
            //Transformamos a postfix la expresión regular
            String postfixRegularExpression = new InfixToPostfix(inputRegularExpression).applyTransformation();
            //Creamos el autómata utilizando la expresión regular
            NondeterministicFiniteAutomata automata = new NondeterministicFiniteAutomata(postfixRegularExpression);
            createGraphUsingNFA(automata);
        } catch (Exception e) {
            adfImagePath = "empty.png";
        }
    }

    private void createGraphUsingNFA(NondeterministicFiniteAutomata automata) throws IOException {
        //Obtenemos el path en el que generaremos el archivo dot y la imagen del adf
        ServletContext servletContext = (ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext();
        String path = servletContext.getRealPath("/") + "res/";
        //Obtenemos el id de la session para poder generar archivos dot e imágenes por cliente conectado
        HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
        String sessionId = session.getId();
        createDotFile(automata, sessionId, path);
        createImageUsingDotFile(sessionId, path);
    }

    public void adfToRegularExpression() {
        setLastConversion("adf");
        try {
            //Lo primero es separar el autómata por puntos y coma para obtener el estado inicial, los estados de aceptación y finalmente las transiciones.
            String[] adfParts = adfTable.split(";");
            //Ahora creamos los arreglos que espera el constructor de la clase NondeterministicFiniteAutomata
            String[] states = adfParts[0].replace("states->", "").split(",");
            String firstState = adfParts[1].replace("start->", "");
            //No hay estados de aceptación y por lo tanto estamos representando al conjunto vacío
            if (adfParts[2].equals("aceptance->")) {
                outputRegularExpression = CONJUNTO_VACIO;
                adfImagePath = "empty.png";
                return;
            }
            String[] aceptanceStates = adfParts[2].replace("aceptance->", "").split("_");
            String[] transitions = adfParts[3].split("#");
            //Eliminamos cualquier transición vacía que haya generado el split
            List<String> list = new ArrayList<String>(Arrays.asList(transitions));
            list.removeAll(Arrays.asList(""));
            transitions = list.toArray(new String[]{});
            //Para obtener una expresión regular a partir de un automata tenemos que usar un autómata generalizado así que creamos el NFA primero
            NondeterministicFiniteAutomata nfa = new NondeterministicFiniteAutomata(states, firstState, aceptanceStates, transitions);
            nfa.generalizeAutomata();
            createGraphUsingNFA(nfa);
            //Ahora creamos el autómata generalizado usando el NFA
            outputRegularExpression = nfa.generateRegularExpressionFromAutomata();

        } catch (Exception ex) {
            outputRegularExpression = CONJUNTO_VACIO;
            adfImagePath = "empty.png";
        }
    }

    /**
     * @return the regularExpression
     */
    public String getInputRegularExpression() {
        return inputRegularExpression;
    }

    /**
     * @param inputRegularExpression the regularExpression to set
     */
    public void setInputRegularExpression(String inputRegularExpression) {
        this.inputRegularExpression = inputRegularExpression;
    }

    /**
     * @return the adf
     */
    public String getAdf() {
        return adf;
    }

    /**
     * @param adf the adf to set
     */
    public void setAdf(String adf) {
        this.adf = adf;
    }

    /**
     * @return the adfImagePath
     */
    public String getAdfImagePath() {
        return adfImagePath;
    }

    /**
     * @param adfImagePath the adfImagePath to set
     */
    public void setAdfImagePath(String adfImagePath) {
        this.adfImagePath = adfImagePath;
    }

    private void createDotFile(NondeterministicFiniteAutomata automata, String sessionId, String path) {
        BufferedWriter dotWriter = null;
        try {
            //Convertimos el autómata a una expresión dot que puede graficar graphiz
            final String dotRepresentation = automata.getDotRepresentation();
            //Enviamos el string al archivo dot del cliente, asegurándonos de eliminar algún archivo anterior
            final String clientDotFile = path + sessionId + "adf.dot";
            File dotFile = new File(clientDotFile);
            dotFile.delete();
            File dotNewFile = new File(clientDotFile);
            dotNewFile.createNewFile();
            dotWriter = new BufferedWriter(new FileWriter(clientDotFile));
            dotWriter.write(dotRepresentation);
        } catch (Exception e) {
            System.out.print("Error al escribir el archivo dot\nCause: " + e.getCause());
        } finally {
            try {
                dotWriter.close();
            } catch (Exception e) {
                System.out.print("Error al cerrar el archivo dot\nCause: " + e.getCause());
            }
        }
    }

    private void createImageUsingDotFile(String sessionId, String path) throws IOException {
        adfImagePath = sessionId + "_adf_" + new Random().nextLong() + ".png";
        String[] command = new String[]{"dot", path + sessionId + "adf.dot", "-Tpng", "-o", path + adfImagePath};
        ProcessExecutor.execute(command);
    }

    /**
     * @return the lastConversion
     */
    public String getLastConversion() {
        return lastConversion;
    }

    /**
     * @param lastConversion the lastConversion to set
     */
    public void setLastConversion(String lastConversion) {
        this.lastConversion = lastConversion;
    }

    /**
     * @return the adfTable
     */
    public String getAdfTable() {
        return adfTable;
    }

    /**
     * @param adfTable the adfTable to set
     */
    public void setAdfTable(String adfTable) {
        this.adfTable = adfTable;
    }

    /**
     * @return the outputRegularExpression
     */
    public String getOutputRegularExpression() {
        return outputRegularExpression;
    }

    /**
     * @param outputRegularExpression the outputRegularExpression to set
     */
    public void setOutputRegularExpression(String outputRegularExpression) {
        this.outputRegularExpression = outputRegularExpression;
    }
}
